package com.hisense.ovcloud.apicontrol.service;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.hisense.ovcloud.apicontrol.config.Constants;
import com.hisense.ovcloud.apicontrol.config.LinkedHashMapSerializer;
import com.hisense.ovcloud.apicontrol.dto.req.CheckReplay6Req;
import com.hisense.ovcloud.apicontrol.dto.req.ValidateReq;
import com.hisense.ovcloud.apicontrol.errors.ResForGW;
import com.hisense.ovcloud.apicontrol.errors.SystemError;
import com.hisense.ovcloud.apicontrol.tool.CheckEmptyUtil;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

import static com.hisense.ovcloud.apicontrol.config.Constants.SecurityParamters.SECURITY_START_VER;
import static com.hisense.ovcloud.apicontrol.tool.PathUtils.ifInEndPoints;

@Service
@Slf4j
public class VerifySignatureService extends BaseAGService{
    @Value("${sign.check.disable:false}")
    private boolean dev;

    @Value("${request.decrypt.endpoints:aaa}")
    private String requestDecryptEndPoints;

    public VerifySignatureService(ApiControlService apiControlService) {
        this.apiControlService = apiControlService;
    }

    @Value("${ignore.verify.signature.endpoints:/email/stop_rev_msg,/v2/api-docs,/wm-web,/hmt-web}")
    private String ignoreVerifySignatureEndpoint;

    @Value("${should.verify.signature.endpoints:/screen-appliance/}")
    private String shouldVerifySignatureEndpoint;

    @Value("${sys.param.gateway.version:7.0}")
    private String newestVersion;

    private final ApiControlService apiControlService;

    public static final ObjectMapper mapper = new ObjectMapper();
    @PostConstruct
    public void init() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); // 保留键顺序
        mapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES); // 不忽略 null 值

        SimpleModule module = new SimpleModule();
        module.addSerializer(LinkedHashMap.class, new LinkedHashMapSerializer());
        mapper.registerModule(module);
    }

    public Mono<ResForGW> run(ValidateReq r, LinkedHashMap<String, Object> parametersMap) {
        ResForGW res = ResForGW.builder().resultCode(Constants.RESULT_CODE_SUCCESS).build();
        if (shouldFilter(r)) {
            if(!shouldFilterSpecialForScreen(r, parametersMap)){
                return Mono.just(res);
            }
            if (r.getMethod().equalsIgnoreCase(Constants.Method.GET)) {
                // uri in decryptEndPoints should not be used via GET method
                if (ifInEndPoints(r.getPath(), requestDecryptEndPoints)) {
                    return Mono.just(setErrorResponse(res, SystemError.REQUEST_MUST_CRYPT));
                }
            }

            if (parametersMap.isEmpty()) {
                if(r.getContentType().contains("multipart/form-data")){
                    return Mono.just(res);
                }else{
                    return Mono.just(setErrorResponse(res, SystemError.MISSPARAM));
                }
            } else {
                if (!parametersMap.containsKey(Constants.PARAM_SIGN) || null == parametersMap.get(Constants.PARAM_SIGN)) {
                    log.error("VerifySignatureService sign not existed! {}", JSONUtil.toJsonStr(parametersMap));
                    return Mono.just(setErrorResponse(res, SystemError.PARAMINVALID.getError(Constants.PARAM_SIGN)));
                } else {
                    long timeStamp = CheckEmptyUtil.isEmpty(parametersMap.get(Constants.PARAM_TIMESTAMP)) ? 0L : Long.parseLong(parametersMap.get(Constants.PARAM_TIMESTAMP).toString());
                    int srcType = CheckEmptyUtil.isEmpty(parametersMap.get(Constants.PARAM_SRCTYPE)) ? 0 : Integer.parseInt(parametersMap.get(Constants.PARAM_SRCTYPE).toString());

                    String sign = Optional.ofNullable(parametersMap.get(Constants.PARAM_SIGN)).orElse("").toString();
                    String randStr = Optional.ofNullable(parametersMap.get(Constants.PARAM_RANDSTR)).orElse("").toString();
                    String version = Optional.ofNullable(parametersMap.get(Constants.VERSION)).orElse("0.0").toString();


                    LinkedHashMap<String, Object> cpParametersMap = new LinkedHashMap<>(parametersMap);
                    cpParametersMap.remove(Constants.PARAM_SIGN);
                    if(r.getMethod().equals(Constants.Method.GET)){
                        if(cpParametersMap.containsKey(Constants.VERSION) && Double.parseDouble(version) >= SECURITY_START_VER){
                            cpParametersMap.remove(Constants.PARAM_TOKEN);
                        }
                    }
                    String paramString = buildParamString(cpParametersMap);

                    log.info("paramString: {}", paramString);

                    if (Double.parseDouble(version) >= SECURITY_START_VER) {
                        if (cpParametersMap.containsKey(Constants.PARAM_APPSECRET)) {
                            return Mono.just(setErrorResponse(res, SystemError.APPSCRETNOTPERMITTED));
                        }

                        String appId = Optional.ofNullable(cpParametersMap.get(Constants.PARAM_APPID)).orElse("").toString();
                        if (StringUtils.isEmpty(appId)) {
                            return Mono.just(setErrorResponse(res, SystemError.PARAMINVALID));
                        }

                        log.debug("after paramString: {}", paramString);

                        return apiControlService.verify6(
                                        CheckReplay6Req.builder()
                                                .appId(appId)
                                                .nonce(randStr)
                                                .timestamp(timeStamp)
                                                .sign(sign)
                                                .version(version)
                                                .paramsStr(paramString)
                                                .srcType(srcType)
                                                .build()
                                ).zipWhen(res1 -> {
                                    res.setResultCode(res1.getResultCode());
                                    if (res1.getResultCode() != Constants.RESULT_CODE_SUCCESS) {
                                        res.setStopForwarding(true);
                                        res.setDesc(res1.getDesc());
                                        log.error("verify failed for paramStr {} of appId {}", appId, paramString);
                                    }
                                    return Mono.just(res);
                                }, (x, y) -> y)
                                .onErrorResume(ex -> {
                                    log.error("VerifySignatureService {} failed! res: {}", r.getOrderedParams(), ex.getMessage());
                                    return Mono.just(setErrorResponse(res, SystemError.VERIFYSIGNATUREERROR));
                                });
                    } else {
                        return apiControlService.verify(
                                        CheckReplay6Req.builder()
                                                .nonce(randStr)
                                                .timestamp(timeStamp)
                                                .sign(sign)
                                                .version(version)
                                                .paramsStr(paramString)
                                                .srcType(srcType)
                                                .build()
                                ).zipWhen(res1 -> {
                                    res.setResultCode(res1.getResultCode());
                                    if (res1.getResultCode() != Constants.RESULT_CODE_SUCCESS) {
                                        res.setDesc(res1.getDesc());
                                        res.setStopForwarding(true);
                                        log.error("verify failed for paramStr {}", paramString);
                                    }
                                    return Mono.just(res);
                                }, (x, y) -> y)
                                .onErrorResume(ex -> {
                                    log.error("VerifySignatureService {} failed! res: {}", r.getOrderedParams(), ex.getMessage());
                                    return Mono.just(setErrorResponse(res, SystemError.VERIFYSIGNATUREERROR));
                                });
                    }
                }
            }
        }
        return Mono.just(res);
    }

    private static String buildParamString(LinkedHashMap<String, Object> originMap) {
        TreeMap<String, String> sortedMap = new TreeMap<>();
        if(!originMap.isEmpty()){
            originMap.forEach((key, value) -> {
                try {
                    if (value instanceof String && !value.toString().isEmpty()) {
                        sortedMap.put(key, value.toString());
                    } else if (value instanceof List) {
                        if (!((List) value).isEmpty()) {
                            sortedMap.put(key, mapper.writeValueAsString(value));

                        }
                    } else if (value instanceof Map) {
                        if (!((Map) value).isEmpty()) {
                            sortedMap.put(key, mapper.writeValueAsString(value));
                        }
                    }else if (value!= null && value.getClass().isArray()) {
                        // 处理数组（如 Object[]）
                        Collection<?> arrayAsList = Arrays.asList((Object[]) value);
                        if(!arrayAsList.isEmpty()){
                            sortedMap.put(key, mapper.writeValueAsString(arrayAsList));
                        }
                    } else {
                        // 其他类型直接转字符串
                        if(value!= null && !value.toString().isEmpty()){
                            sortedMap.put(key, value.toString());
                        }
                    }
                } catch (JsonProcessingException e) {
                    throw new RuntimeException("JSON序列化失败", e);
                }
            });

            return sortedMap.entrySet().stream()
                    .map(entry -> entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.joining("&"));
        }else{
            return "";
        }
    }

    public boolean shouldFilter(ValidateReq r) {
        if (dev) {
            return false;
        }
        return Arrays.stream(ignoreVerifySignatureEndpoint.split(",")).noneMatch(r.getPath()::contains);
    }

    private boolean shouldFilterSpecialForScreen(ValidateReq r, LinkedHashMap<String, Object> sortedMap) {
        if (Arrays.stream(shouldVerifySignatureEndpoint.split(",")).anyMatch(r.getPath()::contains)) {
            if (!sortedMap.containsKey(Constants.VERSION)) {
                return false;
            } else {
                return Double.parseDouble(sortedMap.get(Constants.VERSION).toString()) >= Double.parseDouble(newestVersion);
            }
        } else {
            return true;
        }
    }

    public static void main(String[] args) {
        LinkedHashMap<String, Object> nestedMap = new LinkedHashMap<>();
        nestedMap.put("cusFoodName", "Button mushroom");
        nestedMap.put("foodNum", 1.0);
        nestedMap.put("foodUnitId", 6);
        nestedMap.put("roomId", 9);
        nestedMap.put("roomCode", "freezer");
        nestedMap.put("foodSaveDate", 1744956000);
        nestedMap.put("invalidTime", 7);
        nestedMap.put("enteringSource", 0);
        nestedMap.put("customized", 0);
        nestedMap.put("foodGenreId", 13);
        nestedMap.put("foodId", 232);
        nestedMap.put("commonName", "mushroom");

        ArrayList<LinkedHashMap<String, Object>> fridgeFoods = new ArrayList<>();
        fridgeFoods.add(nestedMap);

        LinkedHashMap<String, Object> c = new LinkedHashMap<String, Object>();
        c.put("fridgeFoods", fridgeFoods);
        c.put("version", 8.1);
        c.put("languageId", 1);
        c.put("randStr", "9e1521d2c58ef5d64811fad4e8dd4405");

//        System.out.println(buildParamString1(c));


    }
}
